import java.text.DateFormat;

import javax.swing.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import javax.mail.*;
import javax.mail.internet.*;

public class Member implements Observer
{
	public static final Map<String, Integer> MAX_BORROW_LIMITS = new HashMap<String, Integer>();
	static
	{
		MAX_BORROW_LIMITS.put("F", 3);
		MAX_BORROW_LIMITS.put("NF", 2);
		MAX_BORROW_LIMITS.put("V", 2);
	};

	public Member(String memberFromFile, Catalog catalog, TransactionRecords transactionRecords) throws ParseException 
	{
		String [] topLevelTokens = memberFromFile.split(",", 0);
		String [] memberProfileTokens = topLevelTokens[0].split(";", 0);
		String [] borrowedBookIdsTokens = null, reservedBookIdsTokens = null;

		if((topLevelTokens.length > 1) && (topLevelTokens[1] != null) && (!topLevelTokens[1].equals("")))
			borrowedBookIdsTokens = topLevelTokens[1].split(";", 0);
		if((topLevelTokens.length > 2) && (topLevelTokens[2] != null) && (!topLevelTokens[1].equals("")))
			reservedBookIdsTokens = topLevelTokens[2].split(";", 0);

		this.memberId = memberProfileTokens[0];
		this.name = memberProfileTokens[1];
		this.email = memberProfileTokens[2];
		this.address = memberProfileTokens[3];
		DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm");
		this.startDate = df.parse(memberProfileTokens[4]);
		this.fineDue = Double.parseDouble(memberProfileTokens[5]);
		this.noOfItemsBorrowedByType.put("F", Integer.parseInt(memberProfileTokens[6]));
		this.noOfItemsBorrowedByType.put("NF", Integer.parseInt(memberProfileTokens[7]));
		this.noOfItemsBorrowedByType.put("V", Integer.parseInt(memberProfileTokens[8]));

		if(borrowedBookIdsTokens != null)
		{
			for(String borrowedBookId : borrowedBookIdsTokens)
			{
				Item item = catalog.getItem(borrowedBookId);
				this.borrowedItems.put(borrowedBookId, item);
			}
		}
		if(reservedBookIdsTokens != null)
		{
			for(String reservedBookId : reservedBookIdsTokens)
			{
				Item item = catalog.getItem(reservedBookId);
				this.reservedItems.put(reservedBookId, item);
				item.addObserver(this);
			}
		}
		this.memberTransactions = transactionRecords.getAllTrasanctionsForMember(this.memberId);
		if(this.memberTransactions == null) //should never be the case; possible only if you directly add members to file/DB
			this.memberTransactions = new ArrayList<Transaction>();
	}

	public Member() 
	{
	}

	public Member(String newMemberId, String name, String address, String email) 
	{
		this.memberId = newMemberId;
		this.name = name;
		this.address = address;
		this.email = email;
		this.startDate = new Date();
		this.fineDue = 0.0;
		this.noOfItemsBorrowedByType.put("F",  0);
		this.noOfItemsBorrowedByType.put("NF",  0);
		this.noOfItemsBorrowedByType.put("V",  0);
	}
	public String serializeMember()
	{
		String memberProfileInfo = "", borrowedItemIdInfo = "", reservedItemIdInfo = "";
		DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm");

		memberProfileInfo = this.memberId + ";" 
				+ this.name + ";"
				+ this.address + ";"
				+ this.email + ";"
				+ df.format(this.startDate) + ";"
				+ this.fineDue + ";"
				+ this.noOfItemsBorrowedByType.get("F") + ";"
				+ this.noOfItemsBorrowedByType.get("NF") + ";"
				+ this.noOfItemsBorrowedByType.get("V");

		if(this.borrowedItems.size()!= 0)
		{
			Object [] itemIds = this.borrowedItems.keySet().toArray();
			borrowedItemIdInfo = (String)itemIds[0];
			for(int i=1; i<this.borrowedItems.size(); ++i)
				borrowedItemIdInfo += ";" + (String)itemIds[i];
		}

		if(this.reservedItems.size()!= 0)
		{
			Object [] itemIds = this.reservedItems.keySet().toArray();
			reservedItemIdInfo = (String)itemIds[0];
			for(int i=1; i<this.reservedItems.size(); ++i)
				reservedItemIdInfo += ";" + (String)itemIds[i];
		}

		return memberProfileInfo + "," + borrowedItemIdInfo + "," + reservedItemIdInfo;
	}

	private String memberId;
	private String name;
	private String email;
	private String address;
	private Date startDate;
	private double fineDue;
	private Map<String, Integer> noOfItemsBorrowedByType = new HashMap<String, Integer>();
	private Map<String, Item> borrowedItems = new HashMap<String, Item>();
	private Map<String, Item> reservedItems = new HashMap<String, Item>();
	private ArrayList<Transaction> memberTransactions = new ArrayList<Transaction>();	

	public void returnItem(String itemId, double fine)
	{
		this.fineDue += fine;
		this.borrowedItems.remove(itemId);
	}

	public Collection<Item> checkout(String itemId, Item item)
	{
		this.borrowedItems.put(itemId, item);
		String itemType = item.getType(); 
		int currentCount = this.noOfItemsBorrowedByType.get(itemType);
		this.noOfItemsBorrowedByType.put(itemType, ++currentCount);

		if(this.reservedItems.containsKey(itemId))
			this.reservedItems.remove(itemId);

		return this.borrowedItems.values();
	}	
	public boolean isEligibleToCheckout(String itemType) 
	{
		if(this.fineDue > 0)
		{
			String message = "You have pending fine of $" + this.fineDue + ", Please pay fine and retry Checkout";
			String title = "PENDING FINE";
			JOptionPane.showMessageDialog(null, message, title, JOptionPane.OK_OPTION);
			return false;
		}

		for(Map.Entry<String, Item> entry : borrowedItems.entrySet())
		{
			Item item = entry.getValue();
			if(item.isOverdue())
			{
				String message = "You have atleast one Overdue item. Please return ALL overdue items, pay fines & retry Checkout";
				String title = "OVERDUE ITEM(S)";
				JOptionPane.showMessageDialog(null, message, title, JOptionPane.OK_OPTION);
				return false;
			}
		}

		if(this.noOfItemsBorrowedByType.get(itemType) == Member.MAX_BORROW_LIMITS.get(itemType))
		{
			String message = "You have reached borrowing limit for this type of item <" + itemType + ">.";
			String title = "BORROWING LIMIT REACHED";
			JOptionPane.showMessageDialog(null, message, title, JOptionPane.OK_OPTION);
			return false;
		}

		return true;
	}
	public Collection<Item> reserve(String itemId, Item item)
	{
		this.reservedItems.put(itemId, item);
		item.addObserver(this);
		return reservedItems.values();
	}
	public double payFine(double fineAmount) 
	{
		fineDue -= fineAmount;
		return fineDue;
	}
	public ArrayList<Transaction> getTransactionHistory() 
	{
		return this.memberTransactions;
	}

	public ArrayList<Transaction> getTransactionHistory(Date startDate, Date endDate) 
	{
		ArrayList<Transaction> memberTransactionsForRange = new ArrayList<Transaction>();

		for(Transaction transaction : this.memberTransactions)
		{
			Date transactionDate = transaction.getDate();
			if(transactionDate.before(startDate))
				continue;

			if(transactionDate.after(endDate))
				continue;

			memberTransactionsForRange.add(transaction);
		}

		return memberTransactionsForRange;
	}

	public void addTransaction(Transaction newTransaction)
	{
		this.memberTransactions.add(newTransaction);			
	}

	public String getMemberId() 
	{
		return this.memberId;
	}

	@Override
	public void update(Observable arg0, Object arg1)
	{
		System.out.println("Yaay, the BOOK has become available");
		Item reservedItem = (Item)arg0;

		try
		{
			//Step1: ===> setup Mail Server Properties
			Properties mailServerProperties = System.getProperties();
			mailServerProperties.put("mail.smtp.port", "587");
			mailServerProperties.put("mail.smtp.auth", "true");
			mailServerProperties.put("mail.smtp.starttls.enable", "true");
			//Mail Server Properties have been setup successfully

			//Step2: ===> get Mail Session
			Session getMailSession = Session.getDefaultInstance(mailServerProperties, null);
			MimeMessage generateMailMessage = new MimeMessage(getMailSession);
			generateMailMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(this.email));
			generateMailMessage.setSubject("Springfield County Library: Item Available for Pickup");
			String emailBody = this.name + ": The Library-Item titled " + reservedItem.getTitle() + " is now AVAILABLE for pick-up";
			generateMailMessage.setContent(emailBody, "text/html");
			//Mail Session has been created successfully

			//Step3: ===> Get Session and Send mail
			Transport transport = getMailSession.getTransport("smtp");
			transport.connect("smtp.gmail.com", "email", "Password");
			transport.sendMessage(generateMailMessage, generateMailMessage.getAllRecipients());
			transport.close();
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
		}
	}		
}
